#pragma once

#include "Socket.hpp"
#include <sys/select.h>

using namespace socket_ns;
using namespace log_ns;

class SelectServer
{
private:
    const static int g_num = sizeof(fd_set) * 8;
    const static int g_defaultfd = -1;

public:
    SelectServer(in_port_t port)
        : _port(port), _listenSocket(std::make_unique<TcpSocket>())
    {
        _listenSocket->BuildListenSocket(_port);
    }

    void InitServer()
    {
        // 初始化 fd 数组
        memset(fd_array, g_defaultfd, sizeof(fd_array));

        // 默认直接添加 _listen_sockfd 到数组中
        fd_array[0] = _listenSocket->GetSockfd();
    }

    // 处理新连接就绪
    void Accepter()
    {
        InetAddr peer;
        int sockfd = _listenSocket->Accepter(&peer); // 一定不会被阻塞
        if (sockfd >= 0)
        {
            LOG(INFO, "Get a new link, client info: [%s], sockfd: %d\n", peer.GetAddrStr().c_str(), sockfd);
            // 将 fd 添加进辅助数组内，该函数结束之后，即可对 select 参数重置
            bool flag = false;
            for (int i = 1; i < g_num; ++i)
            {
                if (fd_array[i] == g_defaultfd)
                {
                    fd_array[i] = sockfd;
                    LOG(INFO, "Add %d to fd_array success!\n", sockfd);
                    flag = true;
                    break;
                }
            }
            if (!flag)
            {
                // 数组已满
                LOG(WARNING, "Server Is Full!\n");
                ::close(sockfd);
            }
        }
        else
        {
            return;
        }
    }

    // 处理普通 fd 就绪
    void HandlerIO(int i)
    {
        char buffer[1024];
        ssize_t n = ::recv(fd_array[i], buffer, sizeof(buffer) - 1, 0);
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << "Client says# \n"
                      << buffer << std::endl;
            std::string content = "<html><body><h1>Hello World</h1></body></html>";
            std::string echo_str = "HTTP/1.0 200 OK\r\n";
            echo_str += "Content-Type: text/html\r\n";
            echo_str += ("Content-Length: " + std::to_string(content.size()) + "\r\n\r\n");
            echo_str += content;
            // echo_str += buffer;
            ::send(fd_array[i], echo_str.c_str(), echo_str.size(), 0);
        }
        else if (n == 0)
        {
            LOG(INFO, "Client quit...\n");
            ::close(fd_array[i]);
            fd_array[i] = g_defaultfd;
        }
        else
        {
            LOG(ERROR, "Recv Error, %d -> %s\n", errno, strerror(errno));
        }
    }

    void HandlerEvent(fd_set &fds)
    {
        // 事件派发 
        for (int i = 0; i < g_num; ++i)
        {
            if (fd_array[i] == g_defaultfd)
                continue;
            // 到这里就是合法 fd，但不一定就绪，得找出就绪的
            if (FD_ISSET(fd_array[i], &fds))
            {
                if (fd_array[i] == _listenSocket->GetSockfd())
                {
                    Accepter();
                }
                else
                {
                    // 普通 IO fd 处理
                    HandlerIO(i);
                }
            }
        }
    }

    void Loop()
    {
        while (true)
        {
            // 接下来就可以进行 accept 获取连接了
            // 但 accept 也是向底层拿数据（连接），还是阻塞式
            // 所以 accept 也是 IO 类调用，只关心新连接的到来，等价于读事件就绪

            // 对文件描述符集进行初始化
            fd_set rfds;
            FD_ZERO(&rfds);
            int max_fd = g_defaultfd;

            // 将合法的 fd 添加进 rfds 集合中
            for (int i = 0; i < g_num; ++i)
            {
                if (fd_array[i] == g_defaultfd)
                    continue;
                FD_SET(fd_array[i], &rfds);
                // 更新出最大的 fd 值
                max_fd = max_fd > fd_array[i] ? max_fd : fd_array[i];
            }

            // 等待策略（时间）
            struct timeval timeout = {3, 0};

            int n = ::select(max_fd + 1, &rfds, nullptr, nullptr, /*&timeout*/ nullptr);
            switch (n)
            {
            case 0:
                LOG(DEBUG, "Time out: %d.%d\n", timeout.tv_sec, timeout.tv_usec);
                break;
            case -1:
                LOG(ERROR, "Select Error, errno: %d -> %s\n", errno, strerror(errno));
                break;
            default:
                LOG(INFO, "Haved event ready, n: %d\n", n);
                // 如果数据就绪但一直不处理，select 会一直通知进程，直至进程处理
                HandlerEvent(rfds);
                PrintDebug();
                // sleep(3);
                break;
            }
        }
    }

    void PrintDebug()
    {
        std::cout << "fd list: ";
        for (int i = 0; i < g_num; ++i)
        {
            if (fd_array[i] == g_defaultfd)
                continue;
            std::cout << fd_array[i] << " ";
        }
        std::cout << std::endl;
    }

    ~SelectServer()
    {
    }

private:
    in_port_t _port; // 服务器怎么能没有端口号
    std::unique_ptr<Socket> _listenSocket;

    // 辅助数组，来保存所有合法 fd
    int fd_array[g_num];
};